<template>
  <NavBar left-arrow @click-left="router.back()" />
  <div class="scope-bar van-hairline--bottom">
    <!-- <div class="custom-tabs">
      <span
        :class="
          selected === 'moveLeft'
            ? 'custom-tab custom-tab--active'
            : 'custom-tab'
        "
        @click="selected = 'moveLeft'"
        >视频</span
      >
      <span
        :class="
          selected === 'moveRight'
            ? 'custom-tab custom-tab--active'
            : 'custom-tab'
        "
        @click="selected = 'moveRight'"
        >文章</span
      >
      
      <div id="slider" :class="selected"></div>
    </div> -->
    <switch-button :ButtonName="switchName" @change="changeSwitch"></switch-button>
  </div>
  <div class="upload-box van-hairline--bottom">
    <div
      class="cover-box"
      v-show="selected === 'moveLeft' && videoList.length !== 0"
    >
      <van-image width="4rem" height="4rem" fit="cover" :src="videoCover" />
      <div class="close-icon" @click="delCover">×</div>
    </div>
    <Uploader
      class="video-uploader"
      :after-read="getCover"
      v-show="selected === 'moveLeft' && videoList.length === 0"
      v-model="videoList"
      accept="video/*"
      :max-count="1"
      :max-size="30 * 1024 * 1024"
      deletable
      @oversize="onOversize"
    />
    <Uploader
      v-show="selected === 'moveRight'"
      v-model="imgList"
      multiple
      :max-count="5"
      deletable
      :before-read="beforeRead"
      :max-size="10 * 1024 * 1024"
      @oversize="onOversize"
    />
  </div>
  <div class="text-box">
    <Field
      v-model="title"
      rows="1"
      autosize
      type="textarea"
      maxlength="20"
      placeholder="标题"
      show-word-limit
    />
    <Field
      v-show="selected === 'moveRight'"
      v-model="desc"
      rows="5"
      autosize
      type="textarea"
      maxlength="500"
      placeholder="请输入内容"
      show-word-limit
    />
  </div>
  <div class="bottom-box">
    <div class="bottom-bar">
      <Button type="primary" round size="large" @click="onPublish"
        >发布作品</Button
      >
    </div>
  </div>
</template>

<script setup>
import "@/assets/css/tabSlider.css";
import { uploadImage } from "@/utils/uploadImage";
import { publishWork } from "@/api/FindModuleAPI/publishWork";
import { base64ToBlob } from "@/utils/convertToBlob";
import { computed, nextTick, ref, watchEffect } from "vue";
import {
  Divider,
  Field,
  NavBar,
  Uploader,
  Button,
  Toast,
  Image as VanImage,
} from "vant";
import { useRouter } from "vue-router";
import { fieldSharedProps } from "vant/lib/field/Field";
import {uploadFileSplice,mergeFile,deleteSpliceFile} from "@/api/uploadFileSplice.js"
import switchButton from "@/components/Common/SwitchButton.vue"
const router = useRouter();
let selected = ref("moveLeft"); //滑块当前类名
let title = ref(""); //发布的标题
let desc = ref(""); //发布内容
const imgList = ref([]); //文章的图片列表
const videoList = ref([]); //发布的视频
let videoCover = ref(""); //视频封面
let switchName = ref(["视频","文章"])
const username = sessionStorage.getItem("username");
let videoCoverBlob = ref("")

const testList = ref([]);

//发布内容的类型
let type = computed(() => {
  return selected.value === "moveLeft" ? "video" : "article";
});
watchEffect(() => {
  if (selected.value) {
    title.value = "";
  }
  if (videoCover.value === "") {
    videoList.value = [];
  }
});
//文件大小超出限制提示
const onOversize = () => {
  let str = type.value === "video" ? "视频" : "图片";
  Toast(`${str}大小不能超过 10MB`);
};
//判断上传图片格式
const beforeRead = file => {
  if (
    file.type === "image/jpeg" ||
    file.type === "image/jpg" ||
    file.type === "image/png"
  ) {
    return true;
  }
  Toast("请上传 jpg / jpeg / png 格式图片");
  return false;
};
/**
 * 加载中状态
 */
const onLoading = () => {
  Toast.loading({
    duration: 0,
    message: "上传中...",
    forbidClick: true,
    loadingType: "spinner",
  });
};
/**
 * 获取视频封面
 */
const getCover = file => {
  //创建视频对象
  const video = document.createElement("video");
  const src = window.URL.createObjectURL(file.file);
  video.crossOrigin = "anonymous";
  video.currentTime = 1;
  video.src = src;
  //视频准备就绪能够播放
  video.oncanplay = () => {
    // 创建画布对象
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    canvas.width = 300;
    canvas.height = 500;
    ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
    videoCover.value = canvas.toDataURL();
    canvas.toBlob((blob)=>[
      videoCoverBlob.value = blob
    ])
    window.URL.revokeObjectURL(src);
  };
};
/**
 * 删除视频封面
 */
const delCover = () => {
  videoCover.value = "";
};
/**
 * 上传视频或图片数组
 */
const uploadFile = async list => {
  const fileList = [];
  for (let i = 0; i < list.length; i++) {
    try {
      const { url } = await uploadImage(list[i].file.name, list[i].file);
      fileList.push(url);
    } catch (error) {
      Toast("上传失败");
    }
  }
  return Promise.resolve(fileList);
};
/**
 * 发布视频作品
 */
const onPublishVideo = async () => {
  onLoading();
  const video = await spliceUploadFile_v2(videoList.value);
  //const blob = base64ToBlob(videoCover.value, "image/png");
  const blob = videoCoverBlob.value;
  console.log(blob);
  const { url } = await uploadImage("cover.png", blob);
  const params = {
    username,
    title: title.value,
    type: type.value,
    status: 0,
    content: video.url,
    cover: url,
    like: 0,
  };
  publishWork(params).then(res => {
    Toast.clear();
    Toast.success("发布成功");
    router.back();
  });
};
/**
 * 发布文章作品
 */
const onPublishArticle = async () => {
  onLoading();
  const res = await uploadFile(imgList.value);
  const params = {
    username,
    title: title.value,
    type: type.value,
    status: 0,
    content: desc.value,
    cover: res[0],
    pic_list: res,
    like: 0,
  };
  publishWork(params).then(res => {
    Toast.clear();
    Toast.success("发布成功");
    router.back();
  });
};

// 上传校验函数
const onPublish = () => {
  if (
    (videoList.value.length === 0 && selected.value === "moveLeft") ||
    (imgList.value.length === 0 && selected.value === "moveRight")
  ) {
    Toast("请上传你的作品");
  } else {
    if (selected.value === "moveLeft") {
      onPublishVideo();
    } else {
      onPublishArticle();
    }
  }
};

const changeSwitch = (index)=>{
    if(index)selected.value="moveRight"
    else selected.value="moveLeft"
}

//小文件分片上传，划分为更小的片，无断点重传
const spliceUploadFile = async(video) => {
    //待上传文件Blob类型
    let file = video[0].file
    //文件类型
    let mine = file.type
    //文件名
    let name = file.name
    //文件分块数组
    let fileChunks = [];
    //文件分块大小 0.05M
    let size = 0.05 * 1024 * 1024;
    //当前hash值
    let index = 0;
    for (let cur = 0; cur < file.size; cur += size) {
      //核心思想:通过index标记顺序，通过slice进行对File对象进行分割处理。
      fileChunks.push({ hash: index++, chunk: file.slice(cur, cur + size) });
    }
    //线程池数组
    let pool = []
    //最大并发量
    let max = 3;
    //结果数组
    let res = []
    //使用表单数据进行提交
    for(let i=0;i<fileChunks.length;i++){
      let myFile = fileChunks[i].chunk;
      let hash = fileChunks[i].hash;
      const formData = new FormData();
      formData.append('myFile',myFile)
      formData.append('hash',hash)
      formData.append('fileName',name)
      //保存Promise
      let task = uploadFileSplice(formData,hash).then((data)=>{
        //移除已完成的Promise
        let index = pool.findIndex((t) => t === task);
        pool.splice(index,1)
        //保存结果
        res.push(data);
      })
      pool.push(task);
      //限制并发等待的核心逻辑
      if(pool.length === max){
        await Promise.race(pool)
      }
    }
    //等待所有Promise执行完毕
    await Promise.all(pool);
    console.log(res);
    //根据Hash值索引进行排序,以防合并文件时顺序不对
    res.sort((a,b) => a.hash-b.hash)
    let params = {
      blobArray:res
    }
    let mfile  = null
    //当上传子文件完毕合并文件
    mfile = await mergeFile(params)
    if(mfile){
       //删除缓存文件：此处可优化，可再利用线程池进行分批删除。
       deleteSpliceFile(params);
    }
    console.log(mfile);
    res = fileChunks = null;
    return mfile;
}

//断点续传文件分片，常用于大文件
const spliceUploadFile_v2 = async(video) =>{

  //待上传文件Blob类型
    let file = video[0].file
    //文件类型
    let mine = file.type
    //文件名
    let name = file.name
    //文件分块数组
    let fileChunks = [];
    //文件分块大小 0.5M
    let size = 0.5 * 1024 * 1024;
    //当前hash值
    let index = 0;
    for (let cur = 0; cur < file.size; cur += size) {
      //核心思想:通过index标记顺序，通过slice进行对File对象进行分割处理。
      fileChunks.push({ hash: index++, chunk: file.slice(cur, cur + size) });
    }
  //结果数组
  let res = []
  //我的文件
  let returnFile = null;

  //最大并发量
  let max = 3; 
  //用于等待函数顺序执行结束否则无法获取到mFile！
  let myResolve
  let promise = new Promise((resolve,reject)=>{
      myResolve = resolve
   })
  //发送分片文件函数
  const uploadFileChunks = async function(list){
     //当队列为空，即所有请求以成功发送，归并文件
     if(list.length === 0){
        res.sort((a,b) => a.hash-b.hash)
        let params = {
            blobArray:res
        }
        let mfile = await mergeFile(params)
        if(mfile){
          //删除缓存文件：此处可优化，可再利用线程池进行分批删除。
            deleteSpliceFile(params);
            returnFile = mfile;
            myResolve();
         }
         return returnFile;
     }
     //线程池数组
    let pool = []
    let failList = [];
    let finish = 0;
    for(let i=0;i<list.length;i++){
      let myFile = list[i].chunk;
      let hash = list[i].hash;
      const formData = new FormData();
      formData.append('myFile',myFile)
      formData.append('hash',hash)
      formData.append('fileName',name)
      //保存Promise
      let task = uploadFileSplice(formData,hash).then((data)=>{
        //移除已完成的Promise
        let index = pool.findIndex((t) => t === task);
        pool.splice(index,1)
        //保存结果
        res.push(data);
      })
      .catch((err)=>{
          //添加失败队列
          console.log("需要重新传输!");
          failList.push(list[i]);
      })
      .finally(()=>{
         //统计当前已发送请求，当已发送请求数目等于列表数目时，重新请求
         finish++;
         if(finish === list.length){
           //当出现失败后减少并发量
           if(failList.length != 0 && max > 1) max--;
           returnFile = uploadFileChunks(failList);
         }
      })
      pool.push(task);
      //限制并发等待的核心逻辑
      if(pool.length === max){
        await Promise.race(pool)
      }
    }
  }
  uploadFileChunks(fileChunks);
  //等待运行结束
  await promise;
  return returnFile
}

</script>

<style lang="less" scoped>
.scope-bar {
  margin-top: 3px;
  padding: 5px 10px;
  background: white;
  display: flex;
  justify-content: flex-end;
  .custom-tabs {
    position: relative;
    width: 80px;
    padding: 3px 0;
    display: flex;
    justify-content: space-around;
    background: #efefef;
    font-size: 10px;
    color: grey;
    .custom-tab {
      z-index: 2;
    }
    .custom-tab--active {
      color: white;
      font-weight: 500;
    }
    #slider {
      position: absolute;
      left: 0;
      top: 1px;
      width: 40px;
      height: calc(100% - 2px);
      background: #3d95ee;
      box-shadow: lightgray 0 0 2px;
      border-radius: 2px;
      z-index: 1;
    }
  }
}
.van-nav-bar {
  :deep(.van-icon) {
    color: #323233;
  }
}
.upload-box {
  display: flex;
  padding: 8px 10px;
  .video-uploader {
    :deep(.van-uploader__upload) {
      width: 4rem;
      height: 4rem;
    }
    :deep(.van-uploader__upload-icon) {
      font-size: 0.8rem;
    }
  }
  .cover-box {
    position: relative;
    .cover {
      width: 100px;
      height: 100px;
      font-size: 14px;
    }
    .close-icon {
      position: absolute;
      top: 0;
      right: 0;
      font-size: 18px;
      padding: 0 4px;
      color: #efefef;
      background: rgba(44, 42, 42, 0.7);
    }
  }
}
.bottom-box {
  height: 60px;
}
.bottom-bar {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 50px;
  padding: 5px 0;
  background: white;
  .van-button {
    width: 90%;
  }
}
.switch{
  top: 9px;
  right: 9%;
}
</style>
